#ifndef FHE_CORE_FUNCDESC_H
#define FHE_CORE_FUNCDESC_H

#include <fhe/core/IFuncDesc.h>
#include <fhe/core/Func.h>

namespace fhe
{
  namespace core
  {
    template <typename T, typename R, BOOST_PP_ENUM_PARAMS( FHE_ARGS, typename A )>
    class FuncDesc;
    
    #define FUNCDESC_id( z, n, data ) data
    
    #define FUNCDESC_iter( z, n, unused ) \
      template <typename T, typename R BOOST_PP_COMMA_IF( n ) BOOST_PP_ENUM_PARAMS( n, typename A )> \
      class FuncDesc<T,R, BOOST_PP_ENUM_PARAMS( n, A ) BOOST_PP_COMMA_IF( n ) BOOST_PP_ENUM( BOOST_PP_SUB( FHE_ARGS, n ), FUNCDESC_id, void )> : public IFuncDesc \
      { \
      public: \
        typedef R (T::*F)( BOOST_PP_ENUM_PARAMS( n, A ) ); \
        FuncDesc( F f, const std::string& name ) \
          : f_( f ) \
          , name_( name ) \
        { \
        } \
        IFunc::Ptr build( Node* node ) const \
        { \
          T* t = dynamic_cast<T*>( node ); \
          FHE_ASSERT_MSG( t != 0, "funcDesc build type mismatch: got %s expected %s", node->type().c_str(), typeid( T ).name() ); \
          return IFunc::Ptr( new Func< T, R, BOOST_PP_ENUM_PARAMS( n, A ) BOOST_PP_COMMA_IF( n ) BOOST_PP_ENUM( BOOST_PP_SUB( FHE_ARGS, n ), FUNCDESC_id, void )>( t, f_, name_ ) ); \
        } \
        std::string name() const \
        { \
          return name_; \
        } \
      private: \
        F f_; \
        std::string name_; \
      }; 
      
    BOOST_PP_REPEAT( FHE_ARGS, FUNCDESC_iter, ~ )

    #undef FUNCDESC_iter

    template <typename T, typename R, BOOST_PP_ENUM_PARAMS( FHE_ARGS, typename A )>
    class ConstFuncDesc;
    
    #define CONSTFUNCDESC_iter( z, n, unused ) \
      template <typename T, typename R BOOST_PP_COMMA_IF( n ) BOOST_PP_ENUM_PARAMS( n, typename A )> \
      class ConstFuncDesc<T,R, BOOST_PP_ENUM_PARAMS( n, A ) BOOST_PP_COMMA_IF( n ) BOOST_PP_ENUM( BOOST_PP_SUB( FHE_ARGS, n ), FUNCDESC_id, void )> : public IFuncDesc \
      { \
      public: \
        typedef R (T::*F)( BOOST_PP_ENUM_PARAMS( n, A ) ) const; \
        ConstFuncDesc( F f, const std::string& name ) \
          : f_( f ) \
          , name_( name ) \
        { \
        } \
        IFunc::Ptr build( Node* node ) const \
        { \
          T* t = dynamic_cast<T*>( node ); \
          FHE_ASSERT_MSG( t != 0, "funcDesc build type mismatch: got %s expected %s", node->type().c_str(), typeid( T ).name() ); \
          return IFunc::Ptr( new ConstFunc< T, R, BOOST_PP_ENUM_PARAMS( n, A ) BOOST_PP_COMMA_IF( n ) BOOST_PP_ENUM( BOOST_PP_SUB( FHE_ARGS, n ), FUNCDESC_id, void )>( t, f_, name_ ) ); \
        } \
        std::string name() const \
        { \
          return name_; \
        } \
      private: \
        F f_; \
        std::string name_; \
      }; 
      
    BOOST_PP_REPEAT( FHE_ARGS, CONSTFUNCDESC_iter, ~ )

    #undef CONSTFUNCDESC_iter
    
    #undef FUNCDESC_id
  }
}

#endif
